// ModelTrainServiceImpl.java
package com.ruoyi.insect.service.impl;

import com.ruoyi.common.dto.TrainingRequest;
import com.ruoyi.insect.domain.DiseaseLabel;
import com.ruoyi.insect.domain.TrainingTask;
import com.ruoyi.insect.mapper.TrainingTaskMapper;
import com.ruoyi.insect.service.IDiseaseLabelService;
import com.ruoyi.insect.service.ITrainingService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Year;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 训练任务Service业务层处理
 *
 * @author ruoyi
 * @date 2024-03-20
 */
@Service
public class TrainingServiceImpl implements ITrainingService {
    private static final Logger log = LoggerFactory.getLogger(TrainingServiceImpl.class);

    @Autowired
    private TrainingTaskMapper trainingTaskMapper;

    @Autowired
    private IDiseaseLabelService diseaseLabelService;

    // 训练任务存储
    private final Map<String, Process> trainingProcesses = new ConcurrentHashMap<>();
    private final Map<String, StringBuilder> trainingLogs = new ConcurrentHashMap<>();


    @Override
    public String exportUserAnnotationsToCoco() {
        try {
            // 获取所有用户标注数据
            DiseaseLabel query = new DiseaseLabel();
            List<DiseaseLabel> labels = diseaseLabelService.selectDiseaseLabelList(query);

            // 构建COCO格式
            Map<String, Object> cocoData = buildCocoFormat(labels);

            // 保存到文件
            String outputPath = "D:/training_output/user_annotations_coco.json";
            File outputFile = new File(outputPath);
            outputFile.getParentFile().mkdirs();

            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(outputFile, cocoData);

            log.info("用户标注数据已导出到: {}", outputPath);
            return outputPath;

        } catch (Exception e) {
            log.error("导出用户标注数据失败", e);
            throw new RuntimeException("导出用户标注数据失败: " + e.getMessage());
        }
    }

    private Map<String, Object> buildCocoFormat(List<DiseaseLabel> labels) {
        Map<String, Object> coco = new HashMap<>();

        // info
        Map<String, Object> info = new HashMap<>();
        info.put("description", "User Annotations COCO Dataset");
        info.put("version", "1.0");
        info.put("year", Year.now().getValue());
        info.put("contributor", "Plant Disease System");
        info.put("date_created", new Date().toString());
        coco.put("info", info);

        // licenses
        List<Map<String, Object>> licenses = new ArrayList<>();
        Map<String, Object> license = new HashMap<>();
        license.put("url", "http://creativecommons.org/licenses/by-nc-sa/2.0/");
        license.put("id", 1);
        license.put("name", "Attribution-NonCommercial-ShareAlike License");
        licenses.add(license);
        coco.put("licenses", licenses);

        // categories - 构建类别映射
        List<Map<String, Object>> categories = buildCategories();
        coco.put("categories", categories);

        // images 和 annotations
        List<Map<String, Object>> images = new ArrayList<>();
        List<Map<String, Object>> annotations = new ArrayList<>();

        int imageId = 1;
        int annotationId = 1;

        for (DiseaseLabel label : labels) {
            if (label.getAnnotationData() != null && !label.getAnnotationData().isEmpty()) {
                try {
                    // 添加image信息
                    Map<String, Object> image = new HashMap<>();
                    image.put("id", imageId);
                    image.put("file_name", label.getOriginalImage());
                    image.put("width", 800); // 默认值，实际应该从图片获取
                    image.put("height", 600); // 默认值
                    image.put("date_captured", label.getPhotoTime() != null ? label.getPhotoTime().toString() : new Date().toString());
                    images.add(image);

                    // 解析标注数据
                    ObjectMapper mapper = new ObjectMapper();
                    Map<String, Object> annotationData = mapper.readValue(label.getAnnotationData(), Map.class);

                    // 处理标注
                    List<Map<String, Object>> labelAnnotations = (List<Map<String, Object>>) annotationData.get("labels");
                    if (labelAnnotations != null) {
                        for (Map<String, Object> ann : labelAnnotations) {
                            Map<String, Object> annotation = new HashMap<>();
                            annotation.put("id", annotationId++);
                            annotation.put("image_id", imageId);

                            // 获取类别ID
                            String categoryName = (String) ann.get("category");
                            int categoryId = getCategoryId(categoryName, categories);
                            annotation.put("category_id", categoryId);

                            // 边界框 [x_min, y_min, x_max, y_max] -> [x, y, width, height]
                            List<Double> bbox = (List<Double>) ann.get("bbox");
                            if (bbox != null && bbox.size() == 4) {
                                double x = bbox.get(0) * 800; // 乘以图片宽度
                                double y = bbox.get(1) * 600; // 乘以图片高度
                                double width = (bbox.get(2) - bbox.get(0)) * 800;
                                double height = (bbox.get(3) - bbox.get(1)) * 600;

                                List<Double> cocoBbox = Arrays.asList(x, y, width, height);
                                annotation.put("bbox", cocoBbox);

                                // 面积
                                double area = width * height;
                                annotation.put("area", area);

                                // segmentation (简化为矩形)
                                List<Double> segmentation = Arrays.asList(x, y, x + width, y, x + width, y + height, x, y + height);
                                annotation.put("segmentation", Arrays.asList(segmentation));

                                annotation.put("iscrowd", 0);

                                annotations.add(annotation);
                            }
                        }
                    }

                    imageId++;

                } catch (Exception e) {
                    log.warn("处理标注数据失败: {}", label.getId(), e);
                }
            }
        }

        coco.put("images", images);
        coco.put("annotations", annotations);

        log.info("构建COCO数据完成: {} 张图片, {} 个标注", images.size(), annotations.size());
        return coco;
    }

    private List<Map<String, Object>> buildCategories() {
        List<Map<String, Object>> categories = new ArrayList<>();

        // 这里应该从数据库或配置中获取所有类别
        String[] diseaseTypes = {"apple_scab_leaf", "apple_leaf", "apple_rust_leaf", "bell_pepper_leaf", "bell_pepper_leaf_spot", "blueberry_leaf", "cherry_leaf", "corn_gray_leaf_spot", "corn_leaf_blight", "corn_rust_leaf", "peach_leaf", "potato_leaf", "potato_leaf_early_blight", "potato_leaf_late_blight", "raspberry_leaf", "soybean_leaf", "squash_powdery_mildew_leaf", "strawberry_leaf", "tomato_early_blight_leaf", "tomato_septoria_leaf_spot", "tomato_leaf", "tomato_leaf_bacterial_spot", "tomato_leaf_late_blight", "tomato_leaf_mosaic_virus", "tomato_leaf_yellow_virus", "tomato_mold_leaf", "tomato_two_spotted_spider_mites_leaf", "grape_leaf", "grape_leaf_black_rot"};

        for (int i = 0; i < diseaseTypes.length; i++) {
            Map<String, Object> category = new HashMap<>();
            category.put("id", i + 1);
            category.put("name", diseaseTypes[i]);
            category.put("supercategory", "plant_disease");
            categories.add(category);
        }

        return categories;
    }

    private int getCategoryId(String categoryName, List<Map<String, Object>> categories) {
        for (Map<String, Object> category : categories) {
            if (categoryName.equals(category.get("name"))) {
                return (Integer) category.get("id");
            }
        }
        return 1; // 默认返回第一个类别
    }

    // 在TrainingServiceImpl.java中更新关键方法，保持原有功能

    @Override
    @Async
    public void startTrainingAsync(TrainingTask task) {
        try {
            // 立即保存任务到数据库并返回响应
            trainingTaskMapper.insertTrainingTask(task);
            trainingLogs.put(task.getId(), new StringBuilder());

            appendLog(task.getId(), "开始模型训练流程...");
            appendLog(task.getId(), "任务ID: " + task.getId());
            appendLog(task.getId(), "任务名称: " + task.getTaskName());

            // 异步执行后续步骤
            CompletableFuture.runAsync(() -> {
                try {
                    // 步骤1: 导出用户标注数据
                    appendLog(task.getId(), "步骤1: 导出用户标注数据...");
                    String cocoPath = exportUserAnnotationsToCoco();
                    appendLog(task.getId(), "用户标注数据已导出到: " + cocoPath);

                    // 步骤2: 数据集分割
                    appendLog(task.getId(), "步骤2: 数据集分割...");
                    String datasetPath = splitDataset();
                    appendLog(task.getId(), "数据集分割完成: " + datasetPath);

                    // 步骤3: 生成训练配置
                    appendLog(task.getId(), "步骤3: 生成训练配置...");
                    String configPath = generateTrainingConfig(datasetPath, task);
                    appendLog(task.getId(), "训练配置生成完成: " + configPath);

                    // 步骤4: 开始训练
                    appendLog(task.getId(), "步骤4: 开始模型训练...");
                    startPaddleTraining(task.getId(), configPath);

                } catch (Exception e) {
                    log.error("训练任务执行失败", e);
                    appendLog(task.getId(), "训练失败: " + e.getMessage());
                    task.setStatus("failed");
                    task.setRemark(e.getMessage());
                    task.setEndTime(new Date());
                    trainingTaskMapper.updateTrainingTask(task);
                }
            });

        } catch (Exception e) {
            log.error("训练任务初始化失败", e);
            // 这里不需要更新任务状态，因为可能还没保存到数据库
        }
    }

    @Override
    public String splitDataset() {
        try {

            appendLog("current", "【诊断】实际执行用户: " + System.getProperty("user.name"));
            appendLog("current", "【诊断】Java环境变量 PATH:\n" + System.getenv("PATH"));

            System.out.println("PATH = " + System.getenv("PATH"));
            System.out.println("当前工作目录 = " + System.getProperty("user.dir"));

            appendLog("current", "🚀 启动数据集分割...");
            File pythonExe = new File("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe");
            File scriptFile = new File("D:/model/split_dataset_fixed.py");

            if (!pythonExe.exists()) {
                throw new RuntimeException("Python 不存在: " + pythonExe.getAbsolutePath());
            }
            if (!scriptFile.exists()) {
                throw new RuntimeException("脚本文件不存在: " + scriptFile.getAbsolutePath());
            }

            // 构建命令
            ProcessBuilder pb = new ProcessBuilder(pythonExe.getAbsolutePath(), scriptFile.getAbsolutePath());
            pb.directory(new File("D:/model"));
            pb.redirectErrorStream(true);

            // 修复 PATH 环境变量
            Map<String, String> env = pb.environment();
            String sysPath = System.getenv("PATH");
            env.put("PATH", pythonExe.getParent() + ";" + pythonExe.getParent() + "\\Scripts;" + System.getenv("PATH"));
            env.put("PYTHONIOENCODING", "utf-8");
            env.put("PYTHONPATH", "D:/PaddleDetection");


            appendLog("current", "执行命令: " + String.join(" ", pb.command()));
            appendLog("current", "当前工作目录: " + pb.directory());
            appendLog("current", "环境变量 PATH: " + env.get("PATH"));

            // 启动子进程
            Process process = pb.start();

            // 读取 Python 输出
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    appendLog("current", line);
                }
            }

            int exitCode = process.waitFor();
            appendLog("current", "Python 退出码: " + exitCode);

            if (exitCode == 0) {
                appendLog("current", "✅ 数据集分割成功！");
                return "D:/training_output/dataset_fixed";
            } else {
                throw new RuntimeException("数据集分割失败，退出码: " + exitCode);
            }

        } catch (Exception e) {
            appendLog("current", "❌ 数据集分割异常: " + e.getMessage());
            log.error("数据集分割异常", e);
            throw new RuntimeException("数据集分割失败: " + e.getMessage(), e);
        }
    }


    private boolean validateDatasetSplit() {
        try {
            File trainAnnotation = new File("D:/training_output/dataset_fixed/annotations/instances_train_t1_mix.json");
            File valAnnotation = new File("D:/training_output/dataset_fixed/annotations/instances_val_all.json");
            File summaryFile = new File("D:/training_output/dataset_fixed/split_incremental_summary.json");

            if (!trainAnnotation.exists() || !valAnnotation.exists() || !summaryFile.exists()) {
                appendLog("current", "❌ 必要文件不存在");
                return false;
            }

            // 读取训练集验证类别一致性
            String trainContent = new String(Files.readAllBytes(trainAnnotation.toPath()), StandardCharsets.UTF_8);
            Map<String, Object> trainData = new ObjectMapper().readValue(trainContent, Map.class);

            List<Map<String, Object>> categories = (List<Map<String, Object>>) trainData.get("categories");
            List<Map<String, Object>> annotations = (List<Map<String, Object>>) trainData.get("annotations");

            // 检查类别ID是否连续且从1开始
            Set<Integer> categoryIds = new HashSet<>();
            for (Map<String, Object> cat : categories) {
                categoryIds.add((Integer) cat.get("id"));
            }

            // 检查标注中的类别ID是否都在类别列表中
            Set<Integer> usedCategoryIds = new HashSet<>();
            for (Map<String, Object> ann : annotations) {
                usedCategoryIds.add((Integer) ann.get("category_id"));
            }

            boolean allIdsValid = categoryIds.containsAll(usedCategoryIds);

            appendLog("current", "类别验证结果:");
            appendLog("current", "  - 总类别数: " + categoryIds.size());
            appendLog("current", "  - 使用的类别ID: " + usedCategoryIds.size());
            appendLog("current", "  - 所有标注类别ID有效: " + allIdsValid);

            return allIdsValid;

        } catch (Exception e) {
            appendLog("current", "❌ 数据集验证失败: " + e.getMessage());
            return false;
        }
    }

    @Override
    public String generateTrainingConfig(String datasetPath, TrainingTask task) {
        try {
            appendLog(task.getId(), "生成训练配置文件...");

            String configOutputPath = "D:/training_output/ppyoloe_incremental.yml";
            String pretrainedPath = "D:/model/ppyoloe_crn_l_obj365_pretrained.pdparams";

            // 检查文件是否存在
            File pretrainFile = new File(pretrainedPath);
            if (!pretrainFile.exists()) {
                throw new RuntimeException("预训练权重文件不存在: " + pretrainedPath);
            }

            File trainAnnotationFile = new File(datasetPath + "/annotations/instances_train_t1_mix.json");
            if (!trainAnnotationFile.exists()) {
                throw new RuntimeException("训练标注文件不存在: " + trainAnnotationFile.getAbsolutePath());
            }

            File splitSummaryFile = new File(datasetPath + "/split_incremental_summary.json");
            if (!splitSummaryFile.exists()) {
                throw new RuntimeException("分割摘要文件不存在: " + splitSummaryFile.getAbsolutePath());
            }

            appendLog(task.getId(), "✅ 使用预训练权重: " + pretrainedPath);
            appendLog(task.getId(), "✅ 训练标注文件: " + trainAnnotationFile.getAbsolutePath());
            appendLog(task.getId(), "✅ 分割摘要文件: " + splitSummaryFile.getAbsolutePath());

            // 构建命令 - 保持原有增量学习功能
            List<String> command = new ArrayList<>();
            command.add("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe");
            command.add("D:/model/policy.py");
            command.add("-a");
            command.add(trainAnnotationFile.getAbsolutePath());
            command.add("--split_json");
            command.add(splitSummaryFile.getAbsolutePath());
            command.add("--incremental");  // 保持增量学习模式
            command.add("--pretrain");
            command.add(pretrainedPath);
            command.add("-o");
            command.add(configOutputPath);

            appendLog(task.getId(), "执行命令: " + String.join(" ", command));

            ProcessBuilder pb = new ProcessBuilder(command);
            pb.directory(new File("D:/model"));
            pb.redirectErrorStream(true);

            Process process = pb.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            StringBuilder output = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
                appendLog(task.getId(), line);
            }

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                appendLog(task.getId(), "训练配置文件生成成功: " + configOutputPath);

                File configFile = new File(configOutputPath);
                if (configFile.exists()) {
                    // 读取配置文件内容进行验证
                    String configContent = new String(Files.readAllBytes(configFile.toPath()), StandardCharsets.UTF_8);
                    appendLog(task.getId(), "配置文件验证成功，文件大小: " + configFile.length() + " 字节");

                    // 检查关键配置
                    if (configContent.contains("collate_batch: false")) {
                        appendLog(task.getId(), "✅ 关键修复: collate_batch已设置为false");
                    }
                    if (configContent.contains("use_gpu: false")) {
                        appendLog(task.getId(), "✅ 配置: 使用CPU模式");
                    }
                }

                return configOutputPath;
            } else {
                throw new RuntimeException("配置文件生成失败，退出码: " + exitCode + "\n输出: " + output.toString());
            }

        } catch (Exception e) {
            log.error("生成训练配置失败", e);
            throw new RuntimeException("生成训练配置失败: " + e.getMessage());
        }
    }

    @Override
    public boolean startPaddleTraining(String taskId, String configPath) {
        try {
            TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
            appendLog(taskId, "🎯 启动 PaddleDetection 训练...");

            File configFile = new File(configPath);
            if (!configFile.exists()) {
                appendLog(taskId, "❌ 配置文件不存在: " + configPath);
                return false;
            }

            File paddleDir = new File("D:/PaddleDetection");
            File trainScript = new File(paddleDir, "tools/train.py");
            if (!trainScript.exists()) {
                appendLog(taskId, "❌ 训练脚本不存在: " + trainScript.getAbsolutePath());
                return false;
            }

            // ✅ 构建命令
            List<String> command = Arrays.asList(
                    "C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe",
                    "-u", // 禁用缓冲
                    "tools/train.py",
                    "-c", configPath,
                    "--eval",
                    "--use_vdl=false",
                    "--opt", "use_shared_memory=false"
            );

            ProcessBuilder pb = new ProcessBuilder(command);
            pb.directory(paddleDir);
            pb.redirectErrorStream(true);

            // ✅ 设置环境变量
            Map<String, String> env = pb.environment();
            env.put("PYTHONUNBUFFERED", "1");
            env.put("PYTHONIOENCODING", "utf-8");
            env.put("PATH", env.get("PATH") + ";C:/Users/1/AppData/Local/Programs/Python/Python310;C:/Users/1/AppData/Local/Programs/Python/Python310/Scripts");

            appendLog(taskId, "🔧 执行命令: " + String.join(" ", command));

            File logFile = new File("D:/training_output/training_log_" + taskId + ".txt");
            logFile.getParentFile().mkdirs();

            Process process = pb.start();
            trainingProcesses.put(taskId, process);

            // ✅ 实时日志读取
            new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
                     BufferedWriter writer = new BufferedWriter(new FileWriter(logFile, true))) {

                    String line;
                    while ((line = reader.readLine()) != null) {
                        writer.write(line + "\n");
                        writer.flush();
                        appendLog(taskId, line);
                    }
                } catch (IOException e) {
                    appendLog(taskId, "❌ 实时日志读取失败: " + e.getMessage());
                }
            }, "Training-LogReader-" + taskId).start();

            // ✅ 启动监控线程（已修复参数）
            monitorTrainingProcess(taskId, process, logFile);

            appendLog(taskId, "✅ 训练进程已启动（实时日志开启）");
            appendLog(taskId, "⚠️ 注意: 使用预训练权重时类别不匹配属正常情况，只加载可匹配部分。");

            return true;

        } catch (Exception e) {
            log.error("启动训练失败", e);
            appendLog(taskId, "❌ 启动训练失败: " + e.getMessage());
            TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
            task.setStatus("failed");
            task.setRemark(e.getMessage());
            task.setEndTime(new Date());
            trainingTaskMapper.updateTrainingTask(task);
            return false;
        }
    }



    private void monitorTrainingProcess(String taskId, Process process, File logFile) {
        new Thread(() -> {
            try {
                TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
                task.setStatus("running");
                trainingTaskMapper.updateTrainingTask(task);

                appendLog(taskId, "🛰️ 正在监控训练进程...");

                // ✅ 记录上一次文件大小，避免重复解析
                long lastSize = 0L;

                // ✅ 启动周期性监控
                while (process.isAlive()) {
                    try {
                        String progress = parseCurrentEpoch(logFile, lastSize);
                        if (progress != null) {
                            appendLog(taskId, progress); // 写日志
                        }

                        // 更新文件大小
                        lastSize = logFile.length();
                        Thread.sleep(3000); // 每3秒检查一次
                    } catch (Exception e) {
                        log.warn("训练进度读取失败：" + e.getMessage());
                    }
                }

                // ✅ 等待训练结束
                int exitCode = process.waitFor();

                if (exitCode == 0) {
                    task.setStatus("completed");
                    appendLog(taskId, "🎉 训练完成！");
                    String finalAccuracy = parseFinalAccuracy(logFile);
                    if (finalAccuracy != null) {
                        task.setAccuracy(Double.parseDouble(finalAccuracy));
                    }
                } else {
                    task.setStatus("failed");
                    appendLog(taskId, "❌ 训练失败，退出码: " + exitCode);
                }

                task.setEndTime(new Date());
                trainingProcesses.remove(taskId);
                trainingTaskMapper.updateTrainingTask(task);

            } catch (Exception e) {
                log.error("监控训练进程失败", e);
                TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
                task.setStatus("failed");
                task.setRemark("监控进程异常: " + e.getMessage());
                task.setEndTime(new Date());
                trainingTaskMapper.updateTrainingTask(task);
            }
        }, "Training-Monitor-" + taskId).start();
    }



    private String parseFinalAccuracy(File logFile) {
        try {
            // 从日志文件最后几行解析准确率
            List<String> lines = Files.readAllLines(logFile.toPath(), StandardCharsets.UTF_8);
            for (int i = lines.size() - 1; i >= Math.max(0, lines.size() - 50); i--) {
                String line = lines.get(i);
                if (line.contains("mAP") || line.contains("accuracy")) {
                    // 简单的正则匹配数字
                    java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("0\\.\\d+");
                    java.util.regex.Matcher matcher = pattern.matcher(line);
                    if (matcher.find()) {
                        return matcher.group();
                    }
                }
            }
        } catch (Exception e) {
            log.warn("解析准确率失败", e);
        }
        return null;
    }

    @Override
    public void stopTraining(String taskId) {
        try {
            Process process = trainingProcesses.get(taskId);
            if (process != null && process.isAlive()) {
                process.destroy();
                appendLog(taskId, "训练任务已被用户停止");

                TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
                task.setStatus("stopped");
                task.setEndTime(new Date());
                trainingTaskMapper.updateTrainingTask(task);
            }
        } catch (Exception e) {
            log.error("停止训练失败", e);
            throw new RuntimeException("停止训练失败: " + e.getMessage());
        }
    }

    @Override
    public TrainingTask getTrainingStatus(String taskId) {
        return trainingTaskMapper.selectTrainingTaskById(taskId);
    }

    @Override
    public List<TrainingTask> getTrainingHistory() {
        TrainingTask query = new TrainingTask();
        return trainingTaskMapper.selectTrainingTaskList(query);
    }

    @Override
    public String getTrainingLog(String taskId) {
        StringBuilder logBuilder = trainingLogs.get(taskId);
        if (logBuilder != null) {
            return logBuilder.toString();
        }

        // 如果内存中没有，从数据库获取
        TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
        return task != null ? task.getLogs() : "暂无日志";
    }

    @Override
    public String getModelPath(String taskId) {
        // 模型输出路径，根据PaddleDetection的默认输出目录
        return "D:/PaddleDetection/output/ppyoloe_inc/model_final.pdparams";
    }

    @Override
    public Map<String, Object> diagnoseSystem() {
        Map<String, Object> result = new HashMap<>();

        try {
            // 检查Python环境
            result.put("python", checkPythonEnvironment());

            // 检查关键文件
            result.put("files", checkRequiredFiles());

            // 检查目录
            result.put("directories", checkDirectories());

            // 检查PaddleDetection环境
            result.put("paddleDetection", checkPaddleDetectionEnvironment());

            // 检查用户数据
            result.put("userData", checkUserData());

            // 总体状态
            Map<String, Object> overallStatus = new HashMap<>();
            boolean systemReady = isSystemReady(result);
            overallStatus.put("systemReady", systemReady);
            overallStatus.put("recommendation", systemReady ? "系统就绪，可以开始训练" : "请检查上述问题后再进行训练");
            result.put("overallStatus", overallStatus);

            // 配置值
            result.put("configValues", getConfigValues());

        } catch (Exception e) {
            log.error("系统诊断失败", e);
            result.put("error", "诊断过程发生错误: " + e.getMessage());
        }

        return result;
    }

    private Map<String, Object> checkPythonEnvironment() {
        Map<String, Object> pythonInfo = new HashMap<>();
        try {
            Process process = Runtime.getRuntime().exec("python --version");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String versionOutput = reader.readLine();
            if (versionOutput == null) {
                reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                versionOutput = reader.readLine();
            }
            pythonInfo.put("version", versionOutput != null ? versionOutput : "未知");
            pythonInfo.put("status", "正常");
        } catch (Exception e) {
            pythonInfo.put("version", "无法获取");
            pythonInfo.put("status", "异常: " + e.getMessage());
        }
        return pythonInfo;
    }

    private Map<String, Object> checkRequiredFiles() {
        Map<String, Object> files = new HashMap<>();

        // 查找实际的预训练权重文件
        String pretrainedPath = findPretrainedWeights();
        boolean pretrainedExists = pretrainedPath != null;

        String[] requiredFiles = {"D:/model/split_dataset_fixed.py", "D:/model/policy.py", "D:/model/old_annotations.json"};

        String[] descriptions = {"数据集分割脚本", "训练配置生成脚本", "原数据集标注文件"};

        for (int i = 0; i < requiredFiles.length; i++) {
            Map<String, Object> fileInfo = new HashMap<>();
            File file = new File(requiredFiles[i]);
            fileInfo.put("exists", file.exists());
            fileInfo.put("path", requiredFiles[i]);
            fileInfo.put("description", descriptions[i]);
            fileInfo.put("readable", file.canRead());
            fileInfo.put("writable", file.canWrite());

            files.put(descriptions[i], fileInfo);
        }

        // 添加预训练权重文件信息
        Map<String, Object> pretrainedInfo = new HashMap<>();
        pretrainedInfo.put("exists", pretrainedExists);
        pretrainedInfo.put("path", pretrainedExists ? pretrainedPath : "C:/Users/1/Desktop/best_model.pdparams");
        pretrainedInfo.put("description", "预训练权重文件");
        pretrainedInfo.put("readable", pretrainedExists);
        pretrainedInfo.put("writable", pretrainedExists);
        files.put("预训练权重文件", pretrainedInfo);

        return files;
    }

    private Map<String, Object> checkDirectories() {
        Map<String, Object> directories = new HashMap<>();

        String[] requiredDirs = {"D:/model/old_images", "D:/ruoyi/uploadPath/upload", "D:/PaddleDetection", "D:/training_output"};

        String[] descriptions = {"原数据集图片目录", "用户上传图片目录", "PaddleDetection目录", "训练输出目录"};

        for (int i = 0; i < requiredDirs.length; i++) {
            Map<String, Object> dirInfo = new HashMap<>();
            File dir = new File(requiredDirs[i]);
            dirInfo.put("exists", dir.exists());
            dirInfo.put("path", requiredDirs[i]);
            dirInfo.put("description", descriptions[i]);

            if (dir.exists()) {
                File[] files = dir.listFiles();
                dirInfo.put("fileCount", files != null ? files.length : 0);
            } else {
                dirInfo.put("fileCount", 0);
            }

            directories.put(descriptions[i], dirInfo);
        }

        return directories;
    }

    private Map<String, Object> checkUserData() {
        Map<String, Object> userData = new HashMap<>();
        try {
            DiseaseLabel query = new DiseaseLabel();
            List<DiseaseLabel> labels = diseaseLabelService.selectDiseaseLabelList(query);

            long totalLabels = labels.size();
            long annotatedLabels = labels.stream().filter(label -> label.getAnnotationData() != null && !label.getAnnotationData().isEmpty()).count();

            userData.put("totalLabels", totalLabels);
            userData.put("annotatedLabels", annotatedLabels);
            userData.put("annotationRate", totalLabels > 0 ? String.format("%.1f%%", (annotatedLabels * 100.0 / totalLabels)) : "0%");

        } catch (Exception e) {
            userData.put("error", "获取用户数据失败: " + e.getMessage());
        }
        return userData;
    }

    private boolean isSystemReady(Map<String, Object> diagnoseResult) {
        // 检查所有必需文件是否存在
        Map<String, Object> files = (Map<String, Object>) diagnoseResult.get("files");
        for (Object fileInfo : files.values()) {
            Map<String, Object> info = (Map<String, Object>) fileInfo;
            if (!(Boolean) info.get("exists")) {
                return false;
            }
        }

        // 检查Python环境
        Map<String, Object> python = (Map<String, Object>) diagnoseResult.get("python");
        if (!"正常".equals(python.get("status"))) {
            return false;
        }

        return true;
    }

    private Map<String, Object> getConfigValues() {
        Map<String, Object> configs = new HashMap<>();
        configs.put("原数据集路径", "D:/model/old_images");
        configs.put("用户图片路径", "D:/ruoyi/uploadPath/upload");
        configs.put("训练输出路径", "D:/training_output");
        configs.put("PaddleDetection路径", "D:/PaddleDetection");
        return configs;
    }

    private void appendLog(String taskId, String message) {
        StringBuilder logBuilder = trainingLogs.get(taskId);
        if (logBuilder == null) {
            logBuilder = new StringBuilder();
            trainingLogs.put(taskId, logBuilder);
        }

        String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        logBuilder.append("[").append(timestamp).append("] ").append(message).append("\n");

        // 同时更新数据库中的日志
        try {
            TrainingTask task = trainingTaskMapper.selectTrainingTaskById(taskId);
            if (task != null) {
                task.setLogs(logBuilder.toString());
                trainingTaskMapper.updateTrainingTask(task);
            }
        } catch (Exception e) {
            log.warn("更新训练任务日志失败: {}", taskId, e);
        }

        log.info("训练任务 {}: {}", taskId, message);
    }

    // 在 TrainingServiceImpl.java 中添加这个方法
    private String findPretrainedWeights() {
        String[] possibleDirs = {"D:/PaddleDetection/pretrain_weights/", "D:/PaddleDetection/pretrained_weights/", "D:/PaddleDetection/weights/", "D:/PaddleDetection/output/", "D:/PaddleDetection/models/", "C:/Users/1/.paddledet/weights/", "D:/model/pretrained/"};

        String[] possibleFiles = {"ppyoloe_crn_l_300e_coco.pdparams", "ppyoloe_crn_m_300e_coco.pdparams", "ppyoloe_crn_s_300e_coco.pdparams", "ppyoloe_plus_crn_l_80e_coco.pdparams", "ppyoloe_plus_crn_m_80e_coco.pdparams", "ppyoloe_plus_crn_s_80e_coco.pdparams", "best_model.pdparams", "model_final.pdparams"};

        for (String dir : possibleDirs) {
            File dirFile = new File(dir);
            if (dirFile.exists() && dirFile.isDirectory()) {
                for (String file : possibleFiles) {
                    File weightFile = new File(dirFile, file);
                    if (weightFile.exists()) {
                        log.info("找到预训练权重文件: {}", weightFile.getAbsolutePath());
                        return weightFile.getAbsolutePath();
                    }
                }

                // 如果没有找到特定文件，尝试查找目录中的任何 .pdparams 文件
                File[] files = dirFile.listFiles((d, name) -> name.endsWith(".pdparams"));
                if (files != null && files.length > 0) {
                    log.info("找到预训练权重文件: {}", files[0].getAbsolutePath());
                    return files[0].getAbsolutePath();
                }
            }
        }

        log.warn("未找到预训练权重文件，将不使用预训练权重");
        return null;
    }

    // 在 TrainingServiceImpl.java 中添加
    private Map<String, Object> checkPaddleDetectionEnvironment() {
        Map<String, Object> paddleInfo = new HashMap<>();

        try {
            File paddleDir = new File("D:/PaddleDetection");
            if (!paddleDir.exists()) {
                paddleInfo.put("status", "PaddleDetection目录不存在");
                return paddleInfo;
            }

            // 检查关键文件
            String[] requiredFiles = {"tools/train.py", "tools/eval.py", "ppdet/modeling/architectures/", "ppdet/data/source/", "configs/ppyoloe/"};

            List<String> missingFiles = new ArrayList<>();
            for (String file : requiredFiles) {
                File f = new File(paddleDir, file);
                if (!f.exists()) {
                    missingFiles.add(file);
                }
            }

            if (!missingFiles.isEmpty()) {
                paddleInfo.put("status", "缺失文件: " + String.join(", ", missingFiles));
            } else {
                paddleInfo.put("status", "正常");
            }

            // 检查Python环境
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "-c", "import paddle; import ppdet; print('PaddlePaddle version:', paddle.__version__)");
            pb.directory(paddleDir);
            Process process = pb.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                paddleInfo.put("pythonEnvironment", "正常");
                paddleInfo.put("paddleVersion", output.toString().trim());
            } else {
                paddleInfo.put("pythonEnvironment", "异常");
                paddleInfo.put("error", "无法导入PaddlePaddle或PaddleDetection");
            }

        } catch (Exception e) {
            paddleInfo.put("status", "检查失败: " + e.getMessage());
        }

        return paddleInfo;
    }

    // 在 TrainingServiceImpl.java 中添加这个调试方法
    public Map<String, Object> debugTrainingEnvironment() {
        Map<String, Object> debugInfo = new HashMap<>();

        try {
            appendLog("debug", "=== 开始调试训练环境 ===");

            // 1. 检查Python环境
            debugInfo.put("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", debugPythonEnvironment());

            // 2. 检查文件系统
            debugInfo.put("filesystem", debugFilesystem());

            // 3. 检查PaddleDetection
            debugInfo.put("paddledetection", debugPaddleDetection());

            // 4. 检查训练脚本
            debugInfo.put("trainingScript", debugTrainingScript());

            // 5. 检查数据目录
            debugInfo.put("dataDirectories", debugDataDirectories());

            // 6. 测试命令执行
            debugInfo.put("commandTest", debugCommandExecution());

            appendLog("debug", "=== 调试完成 ===");

        } catch (Exception e) {
            debugInfo.put("error", "调试过程出错: " + e.getMessage());
        }

        return debugInfo;
    }

    private Map<String, Object> debugPythonEnvironment() {
        Map<String, Object> pythonInfo = new HashMap<>();

        try {
            appendLog("debug", "检查Python环境...");

            // 测试Python命令
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "--version");
            Process process = pb.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String output = reader.readLine();
            int exitCode = process.waitFor();

            pythonInfo.put("pythonCommand", "python");
            pythonInfo.put("exitCode", exitCode);
            pythonInfo.put("output", output);
            pythonInfo.put("status", exitCode == 0 ? "正常" : "异常");

            if (exitCode == 0) {
                appendLog("debug", "✅ Python环境正常: " + output);
            } else {
                appendLog("debug", "❌ Python环境异常");
            }

        } catch (Exception e) {
            pythonInfo.put("error", e.getMessage());
            appendLog("debug", "❌ Python检查失败: " + e.getMessage());
        }

        return pythonInfo;
    }

    private Map<String, Object> debugFilesystem() {
        Map<String, Object> fsInfo = new HashMap<>();
        List<String> issues = new ArrayList<>();

        try {
            appendLog("debug", "检查文件系统...");

            // 检查关键目录和文件
            String[] pathsToCheck = {"D:/model/", "D:/model/split_dataset.py", "D:/model/policy.py", "D:/model/old_annotations.json", "D:/PaddleDetection/", "D:/PaddleDetection/tools/train.py", "D:/training_output/", "D:/ruoyi/uploadPath/upload/"};

            for (String path : pathsToCheck) {
                File file = new File(path);
                Map<String, Object> fileInfo = new HashMap<>();
                fileInfo.put("exists", file.exists());
                fileInfo.put("isDirectory", file.isDirectory());
                fileInfo.put("canRead", file.canRead());
                fileInfo.put("canWrite", file.canWrite());
                fileInfo.put("path", file.getAbsolutePath());

                fsInfo.put(path, fileInfo);

                if (!file.exists()) {
                    issues.add("文件不存在: " + path);
                    appendLog("debug", "❌ 文件不存在: " + path);
                } else {
                    appendLog("debug", "✅ 文件存在: " + path);
                }
            }

        } catch (Exception e) {
            issues.add("文件系统检查失败: " + e.getMessage());
        }

        fsInfo.put("issues", issues);
        return fsInfo;
    }

    private Map<String, Object> debugPaddleDetection() {
        Map<String, Object> pdInfo = new HashMap<>();
        List<String> issues = new ArrayList<>();

        try {
            appendLog("debug", "检查PaddleDetection...");

            File paddleDir = new File("D:/PaddleDetection");
            if (!paddleDir.exists()) {
                issues.add("PaddleDetection目录不存在");
                appendLog("debug", "❌ PaddleDetection目录不存在");
                pdInfo.put("issues", issues);
                return pdInfo;
            }

            // 检查关键文件
            String[] requiredFiles = {"tools/train.py", "tools/eval.py", "ppdet/__init__.py", "configs/__init__.py"};

            for (String filePath : requiredFiles) {
                File file = new File(paddleDir, filePath);
                if (!file.exists()) {
                    issues.add("缺少文件: " + filePath);
                    appendLog("debug", "❌ 缺少文件: " + filePath);
                } else {
                    appendLog("debug", "✅ 文件存在: " + filePath);
                }
            }

            // 测试PaddleDetection导入
            appendLog("debug", "测试PaddleDetection导入...");
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "-c", "import sys; sys.path.append('D:/PaddleDetection); import ppdet; print('PaddleDetection导入成功')");
            Process process = pb.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            StringBuilder output = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }

            int exitCode = process.waitFor();
            if (exitCode == 0) {
                appendLog("debug", "✅ PaddleDetection导入成功");
                pdInfo.put("importTest", "成功");
            } else {
                issues.add("PaddleDetection导入失败");
                appendLog("debug", "❌ PaddleDetection导入失败");
                pdInfo.put("importTest", "失败");

                // 读取错误信息
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                StringBuilder errorOutput = new StringBuilder();
                while ((line = errorReader.readLine()) != null) {
                    errorOutput.append(line).append("\n");
                }
                pdInfo.put("importError", errorOutput.toString());
            }

        } catch (Exception e) {
            issues.add("PaddleDetection检查失败: " + e.getMessage());
            appendLog("debug", "❌ PaddleDetection检查失败: " + e.getMessage());
        }

        pdInfo.put("issues", issues);
        return pdInfo;
    }

    private Map<String, Object> debugTrainingScript() {
        Map<String, Object> scriptInfo = new HashMap<>();

        try {
            appendLog("debug", "检查训练脚本...");

            File scriptFile = new File("D:/model/split_dataset.py");
            if (!scriptFile.exists()) {
                scriptInfo.put("status", "split_dataset.py不存在");
                return scriptInfo;
            }

            // 测试运行分割脚本
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "split_dataset_fixed.py");
            pb.directory(new File("D:/model"));
            Process process = pb.start();

            // 等待进程完成
            boolean finished = process.waitFor(10, TimeUnit.SECONDS);

            if (finished) {
                int exitCode = process.exitValue();
                scriptInfo.put("splitScriptExitCode", exitCode);
                scriptInfo.put("splitScriptStatus", exitCode == 0 ? "成功" : "失败");

                if (exitCode == 0) {
                    appendLog("debug", "✅ 分割脚本测试成功");
                } else {
                    appendLog("debug", "❌ 分割脚本测试失败，退出码: " + exitCode);
                }
            } else {
                scriptInfo.put("splitScriptStatus", "超时");
                appendLog("debug", "❌ 分割脚本测试超时");
                process.destroy();
            }

        } catch (Exception e) {
            scriptInfo.put("error", e.getMessage());
            appendLog("debug", "❌ 训练脚本检查失败: " + e.getMessage());
        }

        return scriptInfo;
    }

    private Map<String, Object> debugDataDirectories() {
        Map<String, Object> dataInfo = new HashMap<>();
        List<String> issues = new ArrayList<>();

        try {
            appendLog("debug", "检查数据目录...");

            // 检查用户数据
            DiseaseLabel query = new DiseaseLabel();
            List<DiseaseLabel> labels = diseaseLabelService.selectDiseaseLabelList(query);
            int annotatedCount = 0;

            for (DiseaseLabel label : labels) {
                if (label.getAnnotationData() != null && !label.getAnnotationData().isEmpty()) {
                    annotatedCount++;
                }
            }

            dataInfo.put("totalLabels", labels.size());
            dataInfo.put("annotatedLabels", annotatedCount);

            if (annotatedCount == 0) {
                issues.add("没有已标注的数据");
                appendLog("debug", "❌ 没有已标注的数据");
            } else {
                appendLog("debug", "✅ 找到 " + annotatedCount + " 个已标注的数据");
            }

            // 检查图片目录
            File uploadDir = new File("D:/ruoyi/uploadPath/upload");
            if (!uploadDir.exists()) {
                issues.add("上传目录不存在");
                appendLog("debug", "❌ 上传目录不存在");
            } else {
                File[] imageFiles = uploadDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".jpg") || name.toLowerCase().endsWith(".jpeg") || name.toLowerCase().endsWith(".png"));

                dataInfo.put("imageFiles", imageFiles != null ? imageFiles.length : 0);
                appendLog("debug", "✅ 上传目录中有 " + (imageFiles != null ? imageFiles.length : 0) + " 个图片文件");
            }

        } catch (Exception e) {
            issues.add("数据目录检查失败: " + e.getMessage());
            appendLog("debug", "❌ 数据目录检查失败: " + e.getMessage());
        }

        dataInfo.put("issues", issues);
        return dataInfo;
    }

    private Map<String, Object> debugCommandExecution() {
        Map<String, Object> commandInfo = new HashMap<>();

        try {
            appendLog("debug", "测试命令执行...");

            // 测试一个简单的Python命令
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "-c", "print('Hello from Python')");
            Process process = pb.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String output = reader.readLine();
            int exitCode = process.waitFor();

            commandInfo.put("testCommand", "python -c \"print('Hello from Python')\"");
            commandInfo.put("exitCode", exitCode);
            commandInfo.put("output", output);

            if (exitCode == 0 && "Hello from Python".equals(output)) {
                commandInfo.put("status", "成功");
                appendLog("debug", "✅ 命令执行测试成功");
            } else {
                commandInfo.put("status", "失败");
                appendLog("debug", "❌ 命令执行测试失败");
            }

        } catch (Exception e) {
            commandInfo.put("error", e.getMessage());
            appendLog("debug", "❌ 命令执行测试失败: " + e.getMessage());
        }

        return commandInfo;
    }

    // 在 TrainingServiceImpl.java 中添加
    public Map<String, Object> testSimpleTraining() {
        Map<String, Object> result = new HashMap<>();

        try {
            appendLog("test", "=== 开始简单训练测试 ===");

            // 创建一个简单的训练任务进行测试
            TrainingTask testTask = new TrainingTask();
            testTask.setId("test_" + System.currentTimeMillis());
            testTask.setTaskName("测试训练任务");
            testTask.setEpochs(1);
            testTask.setBatchSize(1);
            testTask.setModelType("ppyoloe_incremental");
            testTask.setUsePretrained(0);
            testTask.setStatus("running");
            testTask.setStartTime(new Date());

            appendLog("test", "测试任务ID: " + testTask.getId());

            // 直接测试PaddleDetection训练
            boolean success = testPaddleTraining(testTask.getId());

            result.put("success", success);
            result.put("taskId", testTask.getId());
            result.put("message", success ? "测试训练启动成功" : "测试训练启动失败");

        } catch (Exception e) {
            result.put("success", false);
            result.put("error", e.getMessage());
            appendLog("test", "测试训练失败: " + e.getMessage());
        }

        return result;
    }

    private boolean testPaddleTraining(String taskId) {
        try {
            appendLog("test", "测试PaddleDetection训练...");

            File paddleDir = new File("D:/PaddleDetection");
            if (!paddleDir.exists()) {
                appendLog("test", "❌ PaddleDetection目录不存在");
                return false;
            }

            // 创建一个简单的测试配置
            String testConfig = createTestConfig();
            String configPath = "D:/training_output/test_config.yml";

            Files.write(Paths.get(configPath), testConfig.getBytes(StandardCharsets.UTF_8));
            appendLog("test", "创建测试配置文件: " + configPath);

            // 运行训练命令（只运行1个epoch进行测试）
            ProcessBuilder pb = new ProcessBuilder("C:\\Users\\31161\\AppData\\Local\\Programs\\Python\\python38\\python.exe", "tools/train.py", "-c", configPath, "--eval");

            pb.directory(paddleDir);
            appendLog("test", "执行命令: " + String.join(" ", pb.command()));

            // 启动进程
            Process process = pb.start();
            trainingProcesses.put(taskId, process);

            appendLog("test", "✅ 训练进程已启动");

            // 监控进程
            new Thread(() -> {
                try {
                    int exitCode = process.waitFor();
                    appendLog("test", "训练进程退出，退出码: " + exitCode);

                    if (exitCode == 0) {
                        appendLog("test", "🎉 测试训练成功完成");
                    } else {
                        appendLog("test", "❌ 测试训练失败");
                    }

                } catch (Exception e) {
                    appendLog("test", "❌ 监控训练进程失败: " + e.getMessage());
                }
            }).start();

            return true;

        } catch (Exception e) {
            appendLog("test", "❌ 测试训练失败: " + e.getMessage());
            return false;
        }
    }

    private String createTestConfig() {
        return "use_gpu: false\n" + "epoch: 1\n" + "LearningRate:\n" + "  base_lr: 0.001\n" + "  schedulers:\n" + "  - !CosineDecay\n" + "    max_epochs: 1\n" + "  - !LinearWarmup\n" + "    start_factor: 0.1\n" + "    epochs: 1\n" + "OptimizerBuilder:\n" + "  optimizer:\n" + "    type: Momentum\n" + "    momentum: 0.9\n" + "  regularizer:\n" + "    factor: 0.0001\n" + "    type: L2\n" + "architecture: YOLOv3\n" + "YOLOv3:\n" + "  backbone: ResNet\n" + "  neck: FPN\n" + "  yolo_head: YOLOv3Head\n" + "num_classes: 1\n" + "ResNet:\n" + "  depth: 50\n" + "  norm_type: bn\n" + "  freeze_at: 0\n" + "  return_idx: [0,1,2,3]\n" + "FPN:\n" + "  out_channel: 256\n" + "YOLOv3Head:\n" + "  anchor_masks: [[6,7,8],[3,4,5],[0,1,2]]\n" + "  anchors: [[10,13],[16,30],[33,23],[30,61],[62,45],[59,119],[116,90],[156,198],[373,326]]\n" + "  norm_decay: 0.0\n" + "  iou_loss:\n" + "    loss_weight: 2.5\n" + "    max_height: 608\n" + "    max_width: 608\n" + "  iou_aware: false\n" + "  nms:\n" + "    background_label: -1\n" + "    keep_top_k: 100\n" + "    nms_threshold: 0.45\n" + "    nms_top_k: 1000\n" + "    score_threshold: 0.01\n" + "  yolo_loss:\n" + "    loss_weight: 1.0\n" + "metric: COCO\n" + "TrainDataset:\n" + "  !COCODataSet\n" + "    image_dir: images\n" + "    anno_path: annotations/instances_val2017.json\n" + "    dataset_dir: dataset/coco\n" + "    data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']\n" + "EvalDataset:\n" + "  !COCODataSet\n" + "    image_dir: images\n" + "    anno_path: annotations/instances_val2017.json\n" + "    dataset_dir: dataset/coco\n" + "    data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']\n" + "TestDataset:\n" + "  !ImageFolder\n" + "    anno_path: annotations/instances_val2017.json\n" + "TestReader:\n" + "  sample_transforms:\n" + "  - !DecodeImage\n" + "    to_rgb: true\n" + "  - !ResizeImage\n" + "    target_size: 608\n" + "    interp: 2\n" + "  - !NormalizeImage\n" + "    mean: [0.485,0.456,0.406]\n" + "    std: [0.229,0.224,0.225]\n" + "    is_scale: true\n" + "  - !Permute\n" + "    to_bgr: false\n" + "  batch_size: 1\n" + "  shuffle: false\n" + "  drop_last: false\n" + "worker_num: 0\n" + "weights: output/model_final\n" + "log_iter: 1\n" + "save_dir: output\n" + "snapshot_epoch: 1";
    }

    private String parseCurrentEpoch(File logFile, long lastSize) {
        if (!logFile.exists()) return null;

        try (RandomAccessFile raf = new RandomAccessFile(logFile, "r")) {
            long fileSize = raf.length();
            if (fileSize == lastSize) {
                return null; // 没有新内容
            }

            long readStart = Math.max(0, fileSize - 8192); // 只读最后8KB
            raf.seek(readStart);
            String line;

            while ((line = raf.readLine()) != null) {
                String decoded = new String(line.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

                // ✅ 匹配 Epoch 信息（大小写不敏感）
                if (decoded.toLowerCase().contains("epoch")) {
                    Matcher matcher = Pattern.compile("(?i)epoch:\\s*\\[\\s*(\\d+)\\s*/\\s*(\\d+)\\s*\\]").matcher(decoded);
                    if (matcher.find()) {
                        return "⏱ 当前训练进度：Epoch " + matcher.group(1) + "/" + matcher.group(2);
                    }
                }

                // ✅ 匹配 mAP 信息
                if (decoded.toLowerCase().contains("map")) {
                    Matcher matcher = Pattern.compile("mAP.*?(0\\.\\d{2,})").matcher(decoded);
                    if (matcher.find()) {
                        return "📈 当前评估 mAP = " + matcher.group(1);
                    }
                }
            }
        } catch (IOException e) {
            log.warn("解析当前Epoch失败: " + e.getMessage());
        }
        return null;
    }
    @Override
    public String startTraining(TrainingRequest request) {
        return "当前实现版本仅支持本地训练，请调用 startTrainingAsync()";
    }

}
